﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;

namespace StructConver
{
    public class WriteMsg
    {        /// <summary>
             /// 整个字符串的stringbuilder
             /// </summary>
        static StringBuilder sbd = new StringBuilder();
        /// <summary>
        /// 某个类中所有的结构体类型
        /// </summary>
        static List<Type> classAllTypes = new List<Type>();
        /// <summary>
        /// 也解析完成的type集合
        /// </summary>
        static List<Type> listOkType = new List<Type>();
        /// <summary>
        /// 保存结构体长度的数组
        /// </summary>
        static Dictionary<string, int> mapTypeSize = new Dictionary<string, int>();
        static string newClassName;
        static string usingClassName;
        static List<String> listOkNames = new List<string>();
        /// <summary>
        /// 把结构体对象转化为bytes
        /// </summary>
        /// <param name="t"></param>
        public static void CreateClassStructToBytes(Type t)
        {
            sbd.Clear();
            usingClassName = t.Name;
            newClassName = usingClassName+ "_Write.";
            classAllTypes.AddRange(t.GetNestedTypes());
            //构建class代码;
            sbd.AppendLine("using System;");
            sbd.AppendLine(string.Format("using static {0};", usingClassName));
            sbd.AppendLine(string.Format("public class {0}_{1} ", usingClassName, "Write"));
            sbd.AppendLine("{");
            sbd.AppendLine(CreateSwitchTypeSize());
            foreach (Type classType in classAllTypes)
            {
                if (classType.BaseType == typeof(System.Enum))
                {
                    continue;
                }
                if (!listOkType.Contains(classType))
                {
                    string strStruct = FindStructField(classType);
                    sbd.Append(strStruct);
                }
            }
            sbd.AppendLine("}");
            string path = CreateCSFile(sbd.ToString(), t.Name + "_Write");
            Console.WriteLine("文件输出路径="+path);
        }

        static string FindStructField(Type typeStruct)
        {
            //结构体内所有的字段;
            FieldInfo[] fls = typeStruct.GetFields();
            //过滤内嵌使用的结构体;
            FindInternalStruct(fls);
            int structPack = FindStructPack(typeStruct);
            string covStr = null;
            string structName = typeStruct.Name;

            int fieldSize = 0;
            int SizeConst = 0;
            int index = 0;
            int indexAdd = 0;
            double buMin = 0;
            bool isStruct = false;
            bool isArray = false;
            int structSize = Marshal.SizeOf(typeStruct);
            //存入结构体大小的数组;
            mapTypeSize.Add(structName, structSize);
            StringBuilder sbd2 = new StringBuilder();
            if (fls.Length>0)
            {
                sbd2.AppendLine(string.Format("\t public static byte[] Bytes_{0}({1} obj)", structName, typeStruct.FullName.Replace(usingClassName+"+",string.Empty) ));
                sbd2.AppendLine("\t {");
                sbd2.AppendLine(string.Format("\t\t // Marshal.SizeOf({0})={1},Pack={2}", structName, structSize, structPack));
                sbd2.AppendLine(string.Format("\t\t byte[] bytes = new byte[{0}];", structSize));
                sbd2.AppendLine("\t\t int sizeConst = 0; ");
                sbd2.AppendLine("\t\t int index = 0; ");
                foreach (FieldInfo fl in fls)
                {
                    SizeConst = FindFieldSizeConst(fl);
                    isStruct = CheckIsStruct(fl.FieldType);
                    isArray = fl.FieldType.IsArray;
                    if (SizeConst > 1)
                    {   //这个字段是数组 需要查找到他的基础长度;
                        Type t = FindArrayBaseType(fl.FieldType);
                        fieldSize = isStruct ? GetStructTypeBytes(t.Name, out covStr) : GetValueTypeBytes(t.FullName, out covStr);
                        sbd2.AppendLine(string.Format("\t\t sizeConst = {0};", SizeConst));
                        if (!listOkType.Contains(fl.FieldType) && !isStruct)
                        {
                            sbd.AppendLine(CreateAllValueArrayBytes(t));
                            listOkType.Add(fl.FieldType);
                        }
                    }
                    else
                    {
                        fieldSize = isStruct ? mapTypeSize[fl.FieldType.Name] : GetValueTypeBytes(fl.FieldType.FullName, out covStr);
                    }
                    // pack指定的数值和结构(或联合)最大数据成员长度中，比较小的那个进行;
                    buMin = fieldSize <= structPack ? fieldSize : structPack;
                    if (index != 0 && index % buMin != 0 && !isStruct)
                    {
                        index = (int)Math.Ceiling((index / buMin)) * fieldSize;
                    }
                    if (SizeConst > 1)
                    {
                        sbd2.AppendLine(string.Format("\t\t index = {0};", index));
                    }

                    if (isStruct)
                    {
                        //;
                        //sbd2.AppendLine(string.Format("\t\t index={0}", index));
                        string str =isArray ? CreateStructArrayBytes(fl, ref index) : CreateStructBytes(fl, ref index);
                        sbd2.AppendLine(str);
                    }
                    else
                    {
                        if (isArray)
                        {
                            Type t = FindArrayBaseType(fl.FieldType);
                            string head = CheckIsCreateBytes(t.Name);
                            string str = "\t\t byte[] bts_{0} = {1}({2},{3});";
                            str = string.Format(str, fl.Name, head, ("obj."+fl.Name), SizeConst);
                            sbd2.AppendLine(str);
                            sbd2.AppendLine(string.Format("\t\t Array.Copy( {0} , 0, bytes, {1}, {2});", ("bts_"+fl.Name), index, fieldSize*SizeConst) );
                        }
                        else
                        {
                            //这个字段是普通值类型;
                            if (fl.FieldType.Name == "Byte" || fl.FieldType.Name == "SByte")
                            {
                                sbd2.AppendLine(string.Format("\t\t bytes[{0}] = (byte)(obj.{1});", index, fl.Name));
                            }
                            else
                            {
                                covStr = string.Format(covStr, ("obj." + fl.Name));
                                sbd2.AppendLine(string.Format("\t\t Array.Copy( {0} , 0, bytes, {1}, {2});", covStr, index, fieldSize));
                            }

                        }
                        //判断是否数组;
                        indexAdd = isArray ? fieldSize * SizeConst : fieldSize;
                        //本次增量长度;
                        index += indexAdd;
                    }
                    isStruct = false;
                    SizeConst = 0;
                    fieldSize = 0;
                }
                sbd2.AppendLine("\t\t return bytes;");
                sbd2.AppendLine("\t }\n");
                CheckIsCreateStructBytes(newClassName, structName);
            }
            listOkType.Add(typeStruct);
            return sbd2.ToString();
        }

        /// <summary>
        /// 检查是否创建过这个结构体转bytes函数
        /// </summary>
        /// <param name="className"></param>
        /// <param name="structName"></param>
        /// <returns></returns>
        static string CheckIsCreateStructBytes(string className, string structName)
        {
            string head = "Bytes_" + structName;
            foreach (string tmpHead in listOkNames)
            {
                if (tmpHead.Contains(head))
                {   //存在过 直接return;
                    return tmpHead.Replace(className, string.Empty);
                }
            }
            //首次存入 add;
            listOkNames.Add(className + head);
            return head;
        }

        /// <summary>
        /// 检查是否创建过这个bytes化函数了 获取他的名称
        /// </summary>
        /// <param name="typeName"></param>
        /// <returns></returns>
        static string CheckIsCreateBytes(string typeName)
        {
            string head = string.Format("Bytes_{0}Array", typeName);
            foreach (string tmpHead in listOkNames)
            {
                if (tmpHead.Contains("."+head))
                {
                    return tmpHead.Replace(newClassName, string.Empty);
                }
            }
            //没有过 add;
            listOkNames.Add(newClassName  + head);
            return head;
        }

        static string CreateStructBytes(FieldInfo fl, ref int index)
        {
            Type tp = fl.FieldType.IsArray ? FindArrayBaseType(fl.FieldType) : fl.FieldType;
            int size = mapTypeSize[tp.Name];
            string tmp = null;
            //判断index是否是这个结构体中最大基础类型
            int fieldMin = 0;
            int fieldMax = 0;
            int packSize = FindStructPack(tp);
            //查找这个结构体 最短最长的字段;
            FindStructMinMaxField(tp.GetFields(), out fieldMin, out fieldMax);
            if (index % fieldMax != 0)
            {   //计算下标补位到合适的长度;
                index = (int)(Math.Ceiling((index * 1.0f / fieldMax))) * fieldMax;
            }
            string head = CheckIsCreateStructBytes(newClassName, tp.Name);
            string bytesName = "bytes_" + fl.Name;
            tmp += string.Format("\t\t byte[] {0} = {1}({2});\n", bytesName, head, ("obj."+fl.Name ));
            tmp += string.Format("\t\t Array.Copy({0},0, bytes, {1}, {2});", bytesName, index, size);
            index = index + size;
            //验证整体对齐系数 = min((max(int,double,float), 8) = 8，将内存大小由28补齐到8的整数倍32
            int r = Math.Min(packSize, fieldMax);
            if (index % r != 0)
            {
                index = (int)(Math.Ceiling((index * 1.0f / r))) * r;
            }
            return tmp;
        }


        /// <summary>
        /// 结构体数组的bytes读取
        /// </summary>
        /// <returns></returns>
        static string CreateStructArrayBytes(FieldInfo fl, ref int index)
        {
            Type tp = fl.FieldType.IsArray ? FindArrayBaseType(fl.FieldType) : fl.FieldType;
            int size = mapTypeSize[tp.Name];
            string head = CheckIsCreateStructBytes(newClassName, tp.Name);
            string str = null;
            str += "\t\t for(int i=0; i<sizeConst; i++) \n";
            str += "\t\t { \n";
            str += string.Format( "\t\t\t byte[] bts = {0}(obj.{1}[i]); \n", head, fl.Name);
            str += string.Format( "\t\t\t Array.Copy( bts , 0, bytes, index, {0}); \n",  size);
            str += string.Format("\t\t\t index= index +{0};\n", size) ;
            str += "\t\t } \n";
            return str;
        }

        /// <summary>
        /// 基本类型的数组bytes读取
        /// </summary>
        /// <param name="fl"></param>
        /// <param name="index"></param>
        /// <param name="SizeConst"></param>
        /// <returns></returns>
        static string CreateAllValueArrayBytes(Type t)
        {
            //
            string tmp;
            int size = GetValueTypeBytes(t.FullName, out tmp);
            string str = null;
            str += string.Format("\t public static byte[] Bytes_{0}Array({1}[] array, int sizeConst) \n", t.Name, t.Name);
            str += "\t { \n";
            str += "\t\t int index = 0;\n";
            str += string.Format( "\t\t int bitSize = {0}; \n", size);
            str += "\t\t byte[] bytes = new byte[bitSize*sizeConst]; \n";
            str += "\t\t for (int i=0;i<sizeConst; i++)";
            str += "\t\t {\n";
            str += "\t\t\t byte[] bts = BitConverter.GetBytes(array[i]); \n";
            str += "\t\t\t Array.Copy(bts, 0, bytes, index , bitSize); \n";
            str += "\t\t\t index += bitSize; \n";
            str += "\t\t }\n";
            str += "\t\t return bytes; \n";
            str += "\t }";
            return str;
        }

        public static string CreateSwitchTypeSize()
        {
            string str = null;
            str += "\t static int GetBaseValueTypeSize(string typeName) \n";
            str += "\t { \n";
            str += "\t\t switch(typeName){ \n";
            str += string.Format("\t\t case \"{0}\":  \n", "System.Int16" );
            str += "\t\t\t return 2; \n";
            str += string.Format("\t\t case \"{0}\": \n ", "System.Int32");
            str += "\t\t\t return 4; \n";
            str += string.Format("\t\t case \"{0}\": \n ", "System.Int64");
            str += "\t\t\t return 8; \n";
            str += string.Format("\t\t case \"{0}\": \n ", "System.UInt16");
            str += "\t\t\t return 2; \n";
            str += string.Format("\t\t case \"{0}\": \n ", "System.UInt32");
            str += "\t\t\t return 4; \n";
            str += string.Format("\t\t case \"{0}\": \n ", "System.UInt64");
            str += "\t\t\t return 8; \n";
            str += string.Format("\t\t case \"{0}\": \n ", "System.Boolean");
            str += "\t\t\t return 4; \n";
            str += string.Format("\t\t case \"{0}\": \n ", "System.Single");
            str += "\t\t\t return 4; \n";
            str += string.Format("\t\t case \"{0}\": \n ", "System.Double");
            str += "\t\t\t return 8; \n";
            str += string.Format("\t\t case \"{0}\": \n ", "System.Byte");
            str += "\t\t\t return 1; \n";
            str += string.Format("\t\t case \"{0}\": \n ", "System.SByte");
            str += "\t\t\t return 1; \n";
            str +=  "\t\t default: \n ";
            str += "\t\t\t return 0; \n";
            str += "\t\t }\n";
            str += "\t}\n";
            return str;
        }

        /// <summary>
        /// 查找结构体中字段的最小 最大长度
        /// </summary>
        /// <param name="fls"></param>
        /// <param name="min"></param>
        /// <param name="max"></param>
        static void FindStructMinMaxField(FieldInfo[] fls, out int min, out int max)
        {
            min = 0;
            max = 0;
            int sizeField = 0;
            string tmp = null;
            foreach (FieldInfo fl in fls)
            {
                bool isStruct = CheckIsStruct(fl.FieldType);
                if (isStruct)
                {
                    //continue;
                }
                Type type = fl.FieldType.IsArray ? FindArrayBaseType(fl.FieldType) : fl.FieldType;
                sizeField = GetValueTypeBytes(type.ToString(), out tmp);
                if (sizeField == 0 && isStruct && mapTypeSize.ContainsKey(type.Name))
                {
                    sizeField = mapTypeSize[type.Name];
                }
                if (min == 0 && max == 0 && sizeField != 0)
                {
                    min = sizeField;
                    max = sizeField;
                }
                if (sizeField != 0)
                {
                    min = sizeField <= min ? sizeField : min;
                    max = sizeField < max ? max : sizeField;
                }
            }
        }


        /// <summary>
        /// 获得数组的基础类型
        /// </summary>
        /// <param name="t"></param>
        /// <returns></returns>
        static Type FindArrayBaseType(Type t)
        {
            //if (!t.IsArray)
            //    return null;
            string tName = t.FullName.Replace("[]", string.Empty);
            Type elType = t.Assembly.GetType(tName);
            return elType;
        }

        /// <summary>
        /// 查找结构体中数组字段的基础类型长度
        /// </summary>
        /// <param name="fls"></param>
        static int FindArrayBaseField(FieldInfo fl)
        {
            int size = 0;
            string outStr = null;

            Type tp = fl.FieldType;
            if (fl.FieldType.BaseType.Name.Equals("Array"))
            {
                tp = FindArrayBaseType(fl.FieldType);
            }
            size = GetValueTypeBytes(tp.FullName, out outStr);

            return size;
        }

        /// <summary>
        /// 判断是否是结构体类型
        /// </summary>
        /// <param name="t"></param>
        /// <returns></returns>
        static bool CheckIsStruct(Type t)
        {
            string typeName = t.Name.Replace("[]", "").Trim();
            foreach (Type ty in classAllTypes)
            {
                if (typeName == ty.Name)
                {   //这是一个结构体;
                    return true;
                }
            }
            return false;
        }

        /// <summary>
        /// 查找这个字段的数组长度
        /// </summary>
        /// <param name="field"></param>
        /// <returns></returns>
        static int FindFieldSizeConst(FieldInfo field)
        {
            foreach (var m in field.CustomAttributes)
            {
                foreach (var n in m.NamedArguments)
                {
                    if (n.MemberName == "SizeConst")
                    {
                        return (int)n.TypedValue.Value;
                    }
                }
            }
            return 1;
        }

        /// <summary>
        /// 查找结构体设置的Pack对齐长度
        /// </summary>
        /// <param name="type"></param>
        /// <returns></returns>
        static int FindStructPack(Type type)
        {
            StructLayoutAttribute m = type.StructLayoutAttribute;
            return m.Pack;
        }

        /// <summary>
        /// 查找内嵌的结构体
        /// </summary>
        /// <param name="fls"></param>
        static void FindInternalStruct(FieldInfo[] fls)
        {
            string tmp;
            int size = 0;
            foreach (FieldInfo fl in fls)
            {
                size = GetValueTypeBytes(fl.FieldType.FullName, out tmp);
                if (size == 0)
                {   //不属于基本类型;
                    string typeName = fl.FieldType.Name.Replace("[]", "").Trim();
                    foreach (Type ty in classAllTypes)
                    {
                        if (typeName == ty.Name)
                        {
                            //发现需要处理的结构体;
                            if (!listOkType.Contains(ty))
                            {   
                                string strStruct = FindStructField(ty);
                                sbd.Append(strStruct);
                            }

                        }
                    }
                }
            }
        }

        static int GetStructTypeBytes(string type, out string strCov)
        {
            if (mapTypeSize.ContainsKey(type))
            {
                strCov = string.Format("Bytes_{0}({1} obj)", type, type);
                return mapTypeSize[type];
            }
            strCov = "Byte_Error="+type;
            Console.WriteLine("结构体转字节数组失败="+strCov);
            return 0;
        }
 
        static int GetValueTypeBytes(string type, out string bitCov)
        {
            int a = 10;
            byte bt = (byte)a; 
            switch (type)
            {   //返回字节数;
                case "System.Int16":
                    bitCov = "BitConverter.GetBytes({0})";
                    return 2;
                case "System.Int32":
                    bitCov = "BitConverter.GetBytes({0})";
                    return 4;
                case "System.Int64":
                    bitCov = "BitConverter.GetBytes({0})";
                    return 8;
                case "System.UInt16":
                    bitCov = "BitConverter.GetBytes({0})";
                    return 2;
                case "System.UInt32":
                    bitCov = "BitConverter.GetBytes({0})";
                    return 4;
                case "System.UInt64":
                    bitCov = "BitConverter.GetBytes({0})";
                    return 8;
                case "System.Boolean":
                    //服务端bool只有一个字节, 而BOOL长度视实际环境来定，一般可认为是4个字节
                    bitCov = "BitConverter.GetBytes({0})";//BOOL4个字节
                    //bitCov = "BitConverter.ToBoolean(bytes , {0});";//bool1个字节
                    return 4;
                case "System.Byte":
                    bitCov = "(byte){0}";
                    return 1;
                case "System.SByte":
                    bitCov = "(byte){0}";
                    return 1;
                case "System.Char":
                    bitCov = "BitConverter.GetBytes({0})";
                    return 2;
                case "System.Single":
                    bitCov = "BitConverter.GetBytes({0})";
                    return 4;
                case "System.Double":
                    bitCov = "BitConverter.GetBytes({0})";
                    return 8;    
                default:
                    //Console.WriteLine("*********** 发现未知类型= "+type);
                    bitCov = "Read_Error";
                    return 0;
            }
             
        }

        static string CreateCSFile(string content, string fileName)
        {
            string pathRoot = System.Environment.CurrentDirectory;
            string path = pathRoot + "\\" + fileName + ".cs";
            Console.WriteLine(content);
            using (FileStream fileStream = new FileStream(path, FileMode.Create, FileAccess.Write))
            {
                using (TextWriter textWriter = new StreamWriter(fileStream, Encoding.UTF8))
                {
                    textWriter.Write(content);
                }
            }
            return path;
        }

    }
}
